package cn.itcast.nio.c2;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import static cn.itcast.nio.c1.ByteBufferUtil.debugRead;

/**
 * netty服务端
 * 1.阻塞演示
 * 2.通过设置configureBlocking(false)实现非阻塞但cpu空转演示
 *
 */
@Slf4j
public class Server {
    public static void main(String[] args) throws IOException {
        //使用nio来理解阻塞模式

        //0.缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(16);
        //1.创建了服务器   服务器打开socket链接的一个channel  默认是阻塞的
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false); // 设置成非阻塞模式 会让下面的accept直接非阻塞运行的

        //2.绑定监听端口
        ssc.bind(new InetSocketAddress(8080));

        //3.连接集合
        List<SocketChannel> channels = new ArrayList<>();

        //下面改造成非阻塞(异步)以后是可以让代码正常往下执行不会卡住了
        //但是不管有没有连接或者客户端有没有发数据这个线程都会一直true循环去检测，过劳了 会造成cpu空转问题 一直转一直转
        //按道理应该做成 有连接来了就建立连接 有数据再接受数据去处理就好 没有则释放掉资源 而不是让他一直空转卡着
        //所以我们要引入selector 这个selector就是来管理多个channel的它能管理检测channel有无信息发生 有的话就去执行 没有就阻塞住让这个线程休息会
        while (true){ //一直尝试建立连接
            //4.accept 建立与客户端的连接  SocketChannel用来与客户端通信
            // accept默认是阻塞的 接收到了sc才会继续往下执行不然就一直等着直到拿到sc
            // 改成非阻塞后 这里可能sc拿到的是null 因为没客户端连接但是代码继续往下走 所以后面会需要监听器来替我们监听socket
            SocketChannel sc = ssc.accept();
            if(sc != null){
                log.debug("connected....{}",sc);
                sc.configureBlocking(false); //设置channel为非阻塞的让下面的read方法变成非阻塞
                channels.add(sc);
            }
            //5.接受客户端发送的数据
            for (SocketChannel channel :channels) {
                //通过与客户端建立的连接channel把数据读到buffer中 这个方法默认也是阻塞的 可以被改成非阻塞 如果没读到数据返回0
                int read = channel.read(buffer); //返回读到的字节数量
                if(read > 0){
                    buffer.flip();// buffer切换到读模式
                    debugRead(buffer); //打印数据
                    buffer.clear(); //清除且切换为写模式
                    log.debug("after read...{}",channel);
                }
            }
        }

    }

}
